package util

import (
	"errors"
	"net"
	"strconv"
	"strings"
)

var GetIpsMaskContentMap = make(map[string]bool, 2000)

func GetIpInt(ip string) uint32 {
	ips := strings.Split(ip, ".")
	var ipInt uint32
	for i := 0; i < 4; i++ {
		atoi, err := strconv.Atoi(ips[i])
		if err != nil {
			panic(err)
		}
		ipInt = ipInt<<8 | uint32(atoi)
	}
	return ipInt
}

// GetIpsMaskContent just ip in ips
func GetIpsMaskContent(ips, ip string) bool {
	if v, ok := GetIpsMaskContentMap[ips+"_"+ip]; ok {
		return v
	}
	ipInt := GetIpInt(ip)
	split := strings.Split(ips, ",")
	ipList := make([]string, 0)
	ipContentList := make([]string, 0)
	ipMaskList := make([]string, 0)
	for i := range split {
		if strings.ContainsAny(split[i], "/") {
			//ip mask
			ipMaskList = append(ipMaskList, split[i])
		} else if strings.ContainsAny(split[i], "-") {
			//ip content
			ipContentList = append(ipContentList, split[i])
		} else {
			//ip
			ipList = append(ipList, split[i])
		}
	}
	// compare ipList
	for i := range ipList {
		if strings.EqualFold(ipList[i], ip) {
			GetIpsMaskContentMap[ips+"_"+ip] = true
			return true
		}
	}
	//compare ipContent
	for i := range ipContentList {
		contentBool := IsIpInIpContent(ipContentList[i], ipInt)
		if !contentBool {
			continue
		} else {
			GetIpsMaskContentMap[ips+"_"+ip] = contentBool
			return contentBool
		}
	}
	//compare ipMask
	for i := range ipMaskList {
		maskBool := IsIpInIpMask(ipMaskList[i], ipInt)
		if !maskBool {
			continue
		} else {
			GetIpsMaskContentMap[ips+"_"+ip] = maskBool
			return maskBool
		}
	}
	GetIpsMaskContentMap[ips+"_"+ip] = false
	return false
}

func IsIpInIpContent(ipContent string, ipInt uint32) bool {
	split := strings.Split(ipContent, "-")
	if GetIpInt(split[0]) <= ipInt && ipInt <= GetIpInt(split[1]) {
		return true
	}
	return false
}

func IsIpInIpMask(ipMask string, ipInt uint32) bool {
	startIp, endIp := GetCidrIpRange(ipMask)
	if GetIpInt(startIp) <= ipInt && ipInt <= GetIpInt(endIp) {
		return true
	}
	return false
}

func GetCidrIpRange(cidr string) (string, string) {
	ip := strings.Split(cidr, "/")[0]
	ipSegs := strings.Split(ip, ".")
	maskLen, _ := strconv.Atoi(strings.Split(cidr, "/")[1])
	seg3MinIp, seg3MaxIp := GetIpSeg3Range(ipSegs, maskLen)
	seg4MinIp, seg4MaxIp := GetIpSeg4Range(ipSegs, maskLen)
	ipPrefix := ipSegs[0] + "." + ipSegs[1] + "."

	return ipPrefix + strconv.Itoa(seg3MinIp) + "." + strconv.Itoa(seg4MinIp),
		ipPrefix + strconv.Itoa(seg3MaxIp) + "." + strconv.Itoa(seg4MaxIp)
}

// GetIpSeg3Range 得到第三段IP的区间（第一片段.第二片段.第三片段.第四片段）
func GetIpSeg3Range(ipSegs []string, maskLen int) (int, int) {
	if maskLen > 24 {
		segIp, _ := strconv.Atoi(ipSegs[2])
		return segIp, segIp
	}
	ipSeg, _ := strconv.Atoi(ipSegs[2])
	return GetIpSegRange(uint8(ipSeg), uint8(24-maskLen))
}

// GetIpSeg4Range 得到第四段IP的区间（第一片段.第二片段.第三片段.第四片段）
func GetIpSeg4Range(ipSegs []string, maskLen int) (int, int) {
	ipSeg, _ := strconv.Atoi(ipSegs[3])
	segMinIp, segMaxIp := GetIpSegRange(uint8(ipSeg), uint8(32-maskLen))
	return segMinIp + 1, segMaxIp
}

// GetIpSegRange 根据用户输入的基础IP地址和CIDR掩码计算一个IP片段的区间
func GetIpSegRange(userSegIp, offset uint8) (int, int) {
	var ipSegMax uint8 = 255
	netSegIp := ipSegMax << offset
	segMinIp := netSegIp & userSegIp
	segMaxIp := userSegIp&(255<<offset) | ^(255 << offset)
	return int(segMinIp), int(segMaxIp)
}

//GetDeviceId
func GetDeviceId() string {
	ip, err := externalIP()
	if err != nil {
		panic(err)
	}
	interfaces, err := net.Interfaces()
	if err != nil {
		panic(err)
	}

	for _, i := range interfaces {
		byName, err := net.InterfaceByName(i.Name)
		if err != nil {
			panic(err)
		}
		addresses, err := byName.Addrs()
		for _, v := range addresses {
			if strings.Contains(v.String(), ip.String()) {
				return byName.Name
			}
		}
	}
	return ""
}

// get local ip
func externalIP() (net.IP, error) {
	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // loopback interface
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return nil, err
		}
		for _, addr := range addrs {
			ip := getIpFromAddr(addr)
			if ip == nil {
				continue
			}
			return ip, nil
		}
	}
	return nil, errors.New("connected to the network?")
}

func getIpFromAddr(addr net.Addr) net.IP {
	var ip net.IP
	switch v := addr.(type) {
	case *net.IPNet:
		ip = v.IP
	case *net.IPAddr:
		ip = v.IP
	}
	if ip == nil || ip.IsLoopback() {
		return nil
	}
	ip = ip.To4()
	if ip == nil {
		return nil // not an ipv4 address
	}
	return ip
}

// GetIpMaskFromIpSection IP转IP子网掩码 示例: 192.168.7.45-192.168.7.46-> 192.168.7.45/30
func GetIpMaskFromIpSection(sec string) string {
	split := strings.Split(sec, "-")
	startIp := split[0]
	endIp := split[1]

	result := startIp + "/"
	s := ConverToBinary(GetIpInt(startIp))
	e := ConverToBinary(GetIpInt(endIp))
	for i := range s {
		if s[i] != e[i] {
			result += strconv.Itoa(i)
			break
		}
	}
	return result
}

// ConverToBinary 10进制转二进制
func ConverToBinary(n uint32) string {
	result := ""
	for ; n > 0; n /= 2 {
		lsb := n % 2
		result = strconv.Itoa(int(lsb)) + result
	}
	return result
}

func CreateBPF(blockIp string) string {
	var bpf = "tcp and ("
	split := strings.Split(blockIp, ",")
	l := len(split) - 1
	for i := range split {
		s := split[i]
		if strings.ContainsAny(s, "-") {
			s = GetIpMaskFromIpSection(s)
		}
		if i != l {
			bpf += "net " + s + " || "
		} else {
			bpf += "net " + s + ")"
		}

	}
	return bpf
}
